# -*- coding: utf-8 -*-
"""
东方财富API服务
"""

import requests
import json
import logging
from datetime import date, timedelta
from typing import Dict, List, Optional, Any
from models.stock_model import StockInfo
from config.config import EASTMONEY_QUOTE
from utils.redis_manager import redis_manager


class EastMoneyAPIService:
    """东方财富API服务类"""

    def __init__(self):
        self.logger = logging.getLogger(__name__)

        # 字段映射（基于常见的东方财富API字段）
        self.field_mapping = {
            "f2": "current_price",  # 最新价
            "f3": "change_percent",  # 涨跌幅%
            "f4": "change_amount",  # 涨跌额
            "f5": "volume",  # 成交量（手）
            "f6": "turnover",  # 成交额
            "f7": "amplitude",  # 振幅%
            "f8": "turnover_rate",  # 换手率%
            "f9": "pe_ratio",  # 市盈率（动态）
            "f10": "pb_ratio",  # 市净率
            "f12": "code",  # 股票代码
            "f14": "name",  # 股票名称
            "f15": "high",  # 最高价
            "f16": "low",  # 最低价
            "f17": "open_price",  # 开盘价
            "f18": "close_price",  # 昨收价
        }
        
        # 历史数据接口配置（从historical_data_service.py合并）
        self.eastmoney_kline_url = "http://push2his.eastmoney.com/api/qt/stock/kline/get"
        self.eastmoney_kline_params = {
            'ut': 'bd1d9ddb04089700cf9c27f6f7426281',
            'fields1': 'f1,f2,f3,f4,f5,f6',
            'fields2': 'f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61',
            'klt': 101,  # 日K线
            'fqt': 1,    # 前复权
            'end': '20500101',
            'lmt': 10    # 获取最近10天数据
        }

    def get_stock_by_code(self, stock_code: str) -> Optional[StockInfo]:
        """根据股票代码获取单只股票数据"""
        try:
            # 使用批量接口查询单只股票
            result = self.get_stocks_by_codes([stock_code])
            return result.get(stock_code)

        except Exception as e:
            self.logger.error(f"获取股票 {stock_code} 数据失败: {e}")
            return None

    def get_stocks_by_codes(self, stock_codes: List[str]) -> Dict[str, StockInfo]:
        """根据股票代码列表获取多只股票数据（使用批量查询接口）"""
        result: Dict[str, StockInfo] = {}
        try:
            if not stock_codes:
                return result

            # 使用批量查询接口，更高效
            
            # 构造secids参数：市场代码.股票代码
            secids = []
            for code in stock_codes:
                if code.startswith('6'):
                    secids.append(f"1.{code}")  # 沪市
                elif code.startswith(('0', '3')):
                    secids.append(f"0.{code}")  # 深市
                else:
                    self.logger.warning(f"无法识别股票代码市场: {code}")
                    continue
            
            if not secids:
                self.logger.warning("没有有效的股票代码")
                return result
            
            # 构造请求参数
            params = {
                'secids': ','.join(secids),
                'fields': EASTMONEY_QUOTE['fields'],
                'ut': 'bd1d9ddb04089700cf9c27f6f7426281',
                'fltt': 2,
                'invt': 2
            }
            
            self.logger.debug(f"批量查询 {len(secids)} 只股票: {secids[:5]}...")
            response = requests.get(EASTMONEY_QUOTE['quote_url'], params=params, timeout=30)
            response.raise_for_status()

            data = response.json()
            if data and "data" in data and "diff" in data["data"]:
                for item in data["data"]["diff"]:
                    # ==================== DEBUG START ====================
                    if item.get("f12") == "603717":
                        self.logger.info(f"【DEBUG 603717】原始API数据: {item}")
                    # ==================== DEBUG END ======================
                    
                    stock_info = self._parse_stock_data(item, is_batch_api=True)
                    if stock_info and stock_info.code:
                        result[stock_info.code] = stock_info
                
                # 检查哪些股票没有找到
                found_codes = set(result.keys())
                missing_codes = [code for code in stock_codes if code not in found_codes]
                if missing_codes:
                    self.logger.warning(f"未找到股票代码: {missing_codes}")
                
                self.logger.info(f"批量查询成功获取 {len(result)}/{len(stock_codes)} 只股票数据")
            else:
                self.logger.warning("批量查询API响应格式异常")
            
            return result
            
        except Exception as e:
            self.logger.error(f"批量获取股票数据失败: {e}")
            return {}

    def get_stock_names(self, stock_codes: List[str]) -> Dict[str, str]:
        """
        根据股票代码列表批量获取股票名称（带缓存）
        
        Args:
            stock_codes: 股票代码列表
            
        Returns:
            股票代码到名称的映射
        """
        if not stock_codes:
            return {}

        # 1. 从缓存获取
        cached_names = redis_manager.get_stock_names(stock_codes)
        
        # 2. 识别未缓存的股票
        missing_codes = [code for code, name in cached_names.items() if name is None]
        
        result_names = {code: name for code, name in cached_names.items() if name is not None}
        
        # 3. 如果有未缓存的数据，从API获取
        if missing_codes:
            self.logger.info(f"需要从API获取 {len(missing_codes)} 个股票名称")
            api_names = self._fetch_stock_names_from_api(missing_codes)
            
            # 4. 更新缓存和结果
            if api_names:
                redis_manager.set_stock_names(api_names)
                result_names.update(api_names)
        
        # 保证所有输入的code都有一个返回值（即使是空字符串）
        for code in stock_codes:
            if code not in result_names:
                result_names[code] = ''
                
        return result_names

    def _fetch_stock_names_from_api(self, stock_codes: List[str]) -> Dict[str, str]:
        """从东方财富API批量获取股票名称（内部方法，无缓存）"""
        result: Dict[str, str] = {}
        if not stock_codes:
            return result

        try:
            secids = []
            for code in stock_codes:
                if code.startswith('6'):
                    secids.append(f"1.{code}")
                elif code.startswith(('0', '3')):
                    secids.append(f"0.{code}")

            if not secids:
                return result

            params = {
                'secids': ','.join(secids),
                'fields': 'f12,f14',  # 只请求代码和名称
                'ut': 'bd1d9ddb04089700cf9c27f6f7426281',
                'fltt': 2,
                'invt': 2
            }

            response = requests.get(EASTMONEY_QUOTE['quote_url'], params=params, timeout=10)
            response.raise_for_status()

            data = response.json()
            if data and "data" in data and "diff" in data["data"]:
                for item in data["data"]["diff"]:
                    code = item.get("f12")
                    name = item.get("f14")
                    if code and name:
                        result[code] = name
            
            return result
        except Exception as e:
            self.logger.error(f"从API批量获取股票名称失败: {e}")
            return {code: "" for code in stock_codes}

    def _temporarily_disable_proxy(self):
        """临时禁用代理设置"""
        import os
        proxy_vars = ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY']
        original_proxies = {}
        
        for var in proxy_vars:
            if var in os.environ:
                original_proxies[var] = os.environ[var]
                del os.environ[var]
        
        return original_proxies
    
    def _restore_proxy(self, original_proxies):
        """恢复代理设置"""
        import os
        for var, value in original_proxies.items():
            os.environ[var] = value

    def get_historical_stocks_by_codes(self, stock_codes: List[str], target_date: date) -> Dict[str, StockInfo]:
        """根据股票代码列表获取指定日期的历史股票数据
        
        Args:
            stock_codes: 股票代码列表
            target_date: 目标日期
            
        Returns:
            Dict[str, StockInfo]: 股票代码到历史价格信息的映射
        """
        result: Dict[str, StockInfo] = {}
        # 临时禁用代理以提高性能
        original_proxies = self._temporarily_disable_proxy()
        self.logger.info("临时禁用代理以提高历史数据获取性能")
        
        try:
            if not stock_codes:
                return result
                
            # 分批处理，避免一次处理太多股票
            batch_size = 20  # 每批处理20只股票
            self.logger.info(f"开始分批获取 {len(stock_codes)} 只股票的历史数据，批次大小: {batch_size}")
            
            for batch_start in range(0, len(stock_codes), batch_size):
                batch_end = min(batch_start + batch_size, len(stock_codes))
                batch_codes = stock_codes[batch_start:batch_end]
                
                self.logger.info(f"处理批次 {batch_start//batch_size + 1}/{(len(stock_codes) + batch_size - 1)//batch_size}: {batch_start+1}-{batch_end}")
                
                for stock_code in batch_codes:
                    historical_data = self.get_historical_stock_data(stock_code, target_date)
                    if historical_data:
                        # 将历史数据转换为StockInfo格式
                        stock_info = StockInfo(code=stock_code)
                        stock_info.open_price = historical_data.get('open_price', 0)
                        stock_info.close_price = historical_data.get('close_price', 0)
                        stock_info.high = historical_data.get('high_price', 0)
                        stock_info.low = historical_data.get('low_price', 0)
                        stock_info.volume = historical_data.get('volume', 0)
                        result[stock_code] = stock_info
                
                # 每批次之间稍作暂停，避免API限制
                if batch_end < len(stock_codes):
                    import time
                    time.sleep(1)
                    
            self.logger.info(f"历史数据查询成功获取 {len(result)}/{len(stock_codes)} 只股票数据")
            return result
            
        except Exception as e:
            self.logger.error(f"批量获取历史股票数据失败: {e}")
            return {}
        finally:
            # 恢复代理设置
            self._restore_proxy(original_proxies)
            self.logger.info("已恢复代理设置")

    def get_historical_stock_data(self, stock_code: str, target_date: date) -> Optional[Dict[str, Any]]:
        """获取指定股票在指定日期的历史数据
        
        Args:
            stock_code: 股票代码
            target_date: 目标日期
            
        Returns:
            Dict: 包含开盘价、收盘价、最高价、最低价、成交量的字典，获取失败返回None
        """
        try:
            # 转换股票代码格式（东方财富需要市场前缀）
            if stock_code.startswith('6'):
                secid = f"1.{stock_code}"  # 沪市
            else:
                secid = f"0.{stock_code}"  # 深市
                
            # 构造请求参数
            params = self.eastmoney_kline_params.copy()
            params['secid'] = secid
            params['beg'] = target_date.strftime('%Y%m%d')
            params['end'] = target_date.strftime('%Y%m%d')
            
            response = requests.get(self.eastmoney_kline_url, params=params, timeout=10)
            response.raise_for_status()
            
            # 解析JSON数据
            data = response.json()
            if data and 'data' in data and data['data'] and 'klines' in data['data']:
                klines = data['data']['klines']
                target_date_str = target_date.strftime('%Y-%m-%d')
                
                # 查找目标日期的K线数据
                for kline in klines:
                    fields = kline.split(',')
                    if len(fields) >= 6:
                        kline_date = fields[0]  # 日期
                        if kline_date == target_date_str:
                            try:
                                return {
                                    'date': kline_date,
                                    'open_price': float(fields[1]),  # 开盘价
                                    'close_price': float(fields[2]), # 收盘价
                                    'high_price': float(fields[3]),  # 最高价
                                    'low_price': float(fields[4]),   # 最低价
                                    'volume': int(float(fields[5]))  # 成交量
                                }
                            except (ValueError, TypeError):
                                continue
                                
                # 如果没有找到精确日期，尝试找最近的交易日
                if klines:
                    latest_kline = klines[-1]  # 最新的K线数据
                    fields = latest_kline.split(',')
                    if len(fields) >= 6:
                        try:
                            self.logger.debug(f"使用 {stock_code} 最近交易日数据替代")
                            return {
                                'date': fields[0],
                                'open_price': float(fields[1]),
                                'close_price': float(fields[2]),
                                'high_price': float(fields[3]),
                                'low_price': float(fields[4]),
                                'volume': int(float(fields[5]))
                            }
                        except (ValueError, TypeError):
                            pass
                            
            return None
            
        except Exception as e:
            self.logger.debug(f"获取 {stock_code} 历史数据失败: {e}")
            return None

    def get_previous_day_volume(self, stock_code: str, target_date: date = None) -> Optional[int]:
        """获取指定股票的前一交易日成交量
        
        Args:
            stock_code: 股票代码
            target_date: 目标日期，默认为昨天
            
        Returns:
            前一交易日成交量（手），获取失败返回None
        """
        if target_date is None:
            target_date = date.today() - timedelta(days=1)
            
        historical_data = self.get_historical_stock_data(stock_code, target_date)
        if historical_data:
            return historical_data.get('volume')
            
        self.logger.warning(f"无法获取股票 {stock_code} 在 {target_date} 的成交量数据")
        return None

    def get_batch_previous_day_volumes(self, stock_codes: List[str], target_date: date = None) -> Dict[str, Optional[int]]:
        """批量获取多只股票的前一交易日成交量
        
        Args:
            stock_codes: 股票代码列表
            target_date: 目标日期，默认为昨天
            
        Returns:
            股票代码到成交量的映射字典
        """
        if not stock_codes:
            return {}
            
        if target_date is None:
            target_date = date.today() - timedelta(days=1)
            
        result = {}
        historical_stocks = self.get_historical_stocks_by_codes(stock_codes, target_date)
        
        for code in stock_codes:
            if code in historical_stocks:
                result[code] = historical_stocks[code].volume
            else:
                result[code] = None
                
        return result

    def _parse_stock_data(self, item: Dict, is_batch_api: bool = False) -> Optional[StockInfo]:
        """解析单个股票数据
        
        Args:
            item: API返回的股票数据
            is_batch_api: 是否来自批量接口(ulist.np/get)，该接口价格以"分"为单位
        """
        try:
            stock_info = StockInfo(code="")

            for api_field, model_field in self.field_mapping.items():
                if api_field in item and item[api_field] is not None:
                    value = item[api_field]

                    # 处理数值类型
                    if model_field in [
                        "current_price",
                        "open_price",
                        "close_price",
                        "change_percent",
                    ]:
                        try:
                            value = float(value) if value != "-" else 0.0
                            
                            # 🔧 修复：批量接口单位不一致问题
                            if is_batch_api and model_field in ["current_price", "open_price", "close_price"]:
                                # 智能判断单位：如果价格>100，很可能是"分"为单位，需要除以100
                                # 如果价格<=100，很可能已经是"元"为单位，不需要转换
                                if value > 100:
                                    value = value / 100.0
                                    self.logger.debug(f"股票价格{value*100}转换为{value}元（从分转换）")
                            
                            # 涨跌幅处理：批量接口的涨跌幅通常不需要除以100（已经是百分比）
                            elif is_batch_api and model_field == "change_percent":
                                # 涨跌幅字段通常已经是正确的百分比，不需要转换
                                pass
                                
                        except (ValueError, TypeError):
                            value = 0.0
                    elif model_field == "volume":
                        try:
                            value = int(value) if value != "-" else 0
                        except (ValueError, TypeError):
                            value = 0

                    setattr(stock_info, model_field, value)

            # 验证必要字段
            if not stock_info.code:
                return None

            # ==================== DEBUG START ====================
            if stock_info.code == "603717":
                self.logger.info(f"【DEBUG 603717】解析后数据: {stock_info.__dict__}")
            # ==================== DEBUG END ======================

            return stock_info

        except Exception as e:
            self.logger.error(f"解析股票数据失败: {e}, data: {item}")
            return None

    def test_connection(self) -> bool:
        """测试API连接"""
        try:
            response = requests.get(
                self.base_url, params=self.default_params, timeout=10
            )
            response.raise_for_status()
            data = response.json()

            if data and "data" in data:
                self.logger.info("API连接测试成功")
                return True
            else:
                self.logger.error("API连接测试失败: 响应数据格式异常")
                return False

        except Exception as e:
            self.logger.error(f"API连接测试失败: {e}")
            return False
